home *** CD-ROM | disk | FTP | other *** search
/ Programming a Multiplayer FPS in DirectX / Programming a Multiplayer FPS in DirectX (Companion CD).iso / DirectX / dxsdk_oct2004.exe / dxsdk.exe / Samples / C++ / Direct3D / PRTPerVertex / PRTMesh.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2004-09-28  |  32.6 KB  |  827 lines

  1. //--------------------------------------------------------------------------------------
  2. // File: PRTOptionsDlg.cpp
  3. //
  4. // Copyright (c) Microsoft Corporation. All rights reserved.
  5. //--------------------------------------------------------------------------------------
  6. #include "dxstdafx.h"
  7. #include "prtmesh.h"
  8. #include <stdio.h>
  9.  
  10. //#define DEBUG_VS   // Uncomment this line to debug vertex shaders 
  11. //#define DEBUG_PS   // Uncomment this line to debug pixel shaders 
  12.  
  13.  
  14.  
  15. //--------------------------------------------------------------------------------------
  16. CPRTMesh::CPRTMesh(void)
  17. {
  18.     m_pMesh          = NULL;
  19.     m_pPRTBuffer     = NULL;
  20.     m_pPRTCompBuffer = NULL;
  21.     m_pPRTEffect    = NULL;       
  22.     m_pSHIrradEnvMapEffect = NULL;       
  23.     m_pNDotLEffect  = NULL;       
  24.     m_fObjectRadius = 0.0f;
  25.     m_vObjectCenter = D3DXVECTOR3(0,0,0);
  26.     m_aClusterBases = NULL;
  27.     m_dwOrder       = 0;
  28.     m_aPRTConstants = NULL;
  29.     m_pMaterials    = NULL;
  30.     m_dwNumMaterials = 0;
  31.  
  32.     ZeroMemory( &m_ReloadState, sizeof(RELOAD_STATE) );
  33. }
  34.  
  35.  
  36. //--------------------------------------------------------------------------------------
  37. CPRTMesh::~CPRTMesh(void)
  38. {
  39.     Cleanup();
  40. }
  41.  
  42.  
  43. //--------------------------------------------------------------------------------------
  44. HRESULT CPRTMesh::OnCreateDevice( LPDIRECT3DDEVICE9 pd3dDevice )
  45. {
  46.     if( m_ReloadState.bUseReloadState )
  47.     {
  48.         LoadMesh( pd3dDevice, m_ReloadState.strMeshFileName );
  49.         if( m_ReloadState.bLoadCompressed )
  50.         {
  51.             LoadCompPRTBufferFromFile( m_ReloadState.strPRTBufferFileName );
  52.         }
  53.         else
  54.         {
  55.             LoadPRTBufferFromFile( m_ReloadState.strPRTBufferFileName );
  56.             CompressBuffer( m_ReloadState.quality, m_ReloadState.dwNumClusters, m_ReloadState.dwNumPCA );
  57.         }
  58.         ExtractCompressedDataForPRTShader();
  59.         LoadEffects( pd3dDevice, DXUTGetDeviceCaps() );
  60.     }
  61.  
  62.     return S_OK;
  63. }
  64.  
  65.  
  66. //--------------------------------------------------------------------------------------
  67. HRESULT CPRTMesh::OnResetDevice()
  68. {
  69.     HRESULT hr;
  70.     if( m_pPRTEffect )
  71.         V( m_pPRTEffect->OnResetDevice() );
  72.     if( m_pSHIrradEnvMapEffect )
  73.         V( m_pSHIrradEnvMapEffect->OnResetDevice() );
  74.     if( m_pNDotLEffect )
  75.         V( m_pNDotLEffect->OnResetDevice() );
  76.  
  77.     return S_OK;
  78. }
  79.  
  80.  
  81. //--------------------------------------------------------------------------------------
  82. // This function loads the mesh and ensures the mesh has normals; it also optimizes the 
  83. // mesh for the graphics card's vertex cache, which improves performance by organizing 
  84. // the internal triangle list for less cache misses.
  85. //--------------------------------------------------------------------------------------
  86. HRESULT CPRTMesh::LoadMesh( IDirect3DDevice9* pd3dDevice, WCHAR* strMeshFileName )
  87. {
  88.     WCHAR str[MAX_PATH];
  89.     HRESULT hr;
  90.  
  91.     // Release any previous mesh object
  92.     SAFE_RELEASE(m_pMesh);
  93.     SAFE_RELEASE(m_pMaterialBuffer);
  94.     for(int i=0; i<m_pAlbedoTextures.GetSize(); i++ )
  95.     {
  96.         SAFE_RELEASE( m_pAlbedoTextures[i] );
  97.     }
  98.     m_pAlbedoTextures.RemoveAll();
  99.  
  100.     // Load the mesh object
  101.     V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, strMeshFileName ) );
  102.     wcscpy( m_ReloadState.strMeshFileName, str );
  103.     V_RETURN( D3DXLoadMeshFromX( str, D3DXMESH_MANAGED, pd3dDevice, NULL, 
  104.                                  &m_pMaterialBuffer, NULL, &m_dwNumMaterials, &m_pMesh) );
  105.     m_pMaterials = (D3DXMATERIAL*)m_pMaterialBuffer->GetBufferPointer();
  106.  
  107.     // Change the current directory to the mesh's directory so we can
  108.     // find the textures.
  109.     WCHAR* pLastSlash = wcsrchr( str, L'\\' );
  110.     if( pLastSlash )
  111.         *(pLastSlash + 1) = 0;
  112.     WCHAR strCWD[MAX_PATH];
  113.     GetCurrentDirectory( MAX_PATH, strCWD );
  114.     SetCurrentDirectory( str );
  115.  
  116.     // Lock the vertex buffer to get the object's radius & center
  117.     // simply to help position the camera a good distance away from the mesh.
  118.     IDirect3DVertexBuffer9* pVB = NULL;
  119.     void* pVertices;
  120.     V_RETURN( m_pMesh->GetVertexBuffer( &pVB ) );
  121.     V_RETURN( pVB->Lock( 0, 0, &pVertices, 0 ) );
  122.  
  123.     D3DVERTEXELEMENT9 Declaration[MAXD3DDECLLENGTH + 1];
  124.     m_pMesh->GetDeclaration( Declaration );
  125.     DWORD dwStride = D3DXGetDeclVertexSize( Declaration, 0 );
  126.     V_RETURN( D3DXComputeBoundingSphere( (D3DXVECTOR3*)pVertices, m_pMesh->GetNumVertices(), 
  127.                                           dwStride, &m_vObjectCenter, 
  128.                                           &m_fObjectRadius ) );
  129.  
  130.     pVB->Unlock();
  131.     SAFE_RELEASE( pVB );
  132.  
  133.     // Make the mesh have a known decl in order to pass per vertex CPCA 
  134.     // data to the shader
  135.     V_RETURN( AdjustMeshDecl( pd3dDevice, &m_pMesh ) );
  136.  
  137.     // Optimize the mesh for this graphics card's vertex cache 
  138.     // so when rendering the mesh's triangle list the vertices will 
  139.     // cache hit more often so it won't have to re-execute the vertex shader 
  140.     // on those vertices so it will improve perf.     
  141.     DWORD *rgdwAdjacency = NULL;
  142.     rgdwAdjacency = new DWORD[m_pMesh->GetNumFaces() * 3];
  143.     if( rgdwAdjacency == NULL )
  144.         return E_OUTOFMEMORY;
  145.     V( m_pMesh->ConvertPointRepsToAdjacency(NULL, rgdwAdjacency) );
  146.     V( m_pMesh->OptimizeInplace( D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS, 
  147.                                  rgdwAdjacency, NULL, NULL, NULL) );
  148.     delete []rgdwAdjacency;
  149.  
  150.     for(UINT i=0; i<m_dwNumMaterials; i++ )
  151.     {
  152.         // First attempt to look for texture in the same folder as the input folder.
  153.         WCHAR strTextureTemp[MAX_PATH];
  154.         MultiByteToWideChar( CP_ACP, 0, m_pMaterials[i].pTextureFilename, -1, strTextureTemp, MAX_PATH );
  155.         strTextureTemp[MAX_PATH-1] = 0;
  156.  
  157.         // Create the mesh texture from a file
  158.         if( SUCCEEDED( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, strTextureTemp ) ) )
  159.         {
  160.             IDirect3DTexture9* pAlbedoTexture;
  161.             V( D3DXCreateTextureFromFileEx( pd3dDevice, str, D3DX_DEFAULT, D3DX_DEFAULT, 
  162.                                             D3DX_DEFAULT, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, 
  163.                                             D3DX_DEFAULT, D3DX_DEFAULT, 0, 
  164.                                             NULL, NULL, &pAlbedoTexture ) );
  165.             m_pAlbedoTextures.Add( pAlbedoTexture );
  166.         }
  167.         else
  168.         {
  169.             m_pAlbedoTextures.Add( NULL );
  170.         }
  171.     }
  172.    
  173.     SetCurrentDirectory( strCWD );
  174.  
  175.     return S_OK;
  176. }
  177.  
  178.  
  179. //--------------------------------------------------------------------------------------
  180. HRESULT CPRTMesh::SetMesh( IDirect3DDevice9* pd3dDevice, ID3DXMesh* pMesh )
  181. {
  182.     HRESULT hr;
  183.  
  184.     // Release any previous mesh object
  185.     SAFE_RELEASE(m_pMesh);
  186.  
  187.     m_pMesh = pMesh;
  188.  
  189.     V( AdjustMeshDecl( pd3dDevice, &m_pMesh ) );
  190.  
  191.     // Sort the attributes
  192.     DWORD *rgdwAdjacency = NULL;
  193.     rgdwAdjacency = new DWORD[m_pMesh->GetNumFaces() * 3];
  194.     if( rgdwAdjacency == NULL )
  195.         return E_OUTOFMEMORY;
  196.     V( m_pMesh->ConvertPointRepsToAdjacency(NULL, rgdwAdjacency) );
  197.     V( m_pMesh->OptimizeInplace(D3DXMESHOPT_VERTEXCACHE | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_IGNOREVERTS, 
  198.                                 rgdwAdjacency, NULL, NULL, NULL) );
  199.     delete []rgdwAdjacency;
  200.  
  201.     return S_OK;
  202. }
  203.  
  204.  
  205. //-----------------------------------------------------------------------------
  206. // Make the mesh have a known decl in order to pass per vertex CPCA 
  207. // data to the shader
  208. //-----------------------------------------------------------------------------
  209. HRESULT CPRTMesh::AdjustMeshDecl( IDirect3DDevice9* pd3dDevice, ID3DXMesh** ppMesh )
  210. {
  211.     HRESULT hr;
  212.     LPD3DXMESH pInMesh = *ppMesh;
  213.     LPD3DXMESH pOutMesh = NULL;
  214.  
  215.     D3DVERTEXELEMENT9 decl[MAX_FVF_DECL_SIZE] = 
  216.     {
  217.         {0,  0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
  218.         {0,  12, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
  219.         {0,  24, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
  220.         {0,  32, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0},
  221.         {0,  36, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 1},
  222.         {0,  52, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 2},
  223.         {0,  68, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 3},
  224.         {0,  84, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 4},
  225.         {0, 100, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 5},
  226.         {0, 116, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 6},
  227.         D3DDECL_END()
  228.     };
  229.  
  230.     // To do CPCA, we need to store (g_dwNumPCAVectors + 1) scalers per vertex, so 
  231.     // make the mesh have a known decl to store this data.  Since we can't do 
  232.     // skinning and PRT at once, we use D3DDECLUSAGE_BLENDWEIGHT[0] 
  233.     // to D3DDECLUSAGE_BLENDWEIGHT[6] to store our per vertex data needed for PRT.
  234.     // Notice that D3DDECLUSAGE_BLENDWEIGHT[0] is a float1, and
  235.     // D3DDECLUSAGE_BLENDWEIGHT[1]-D3DDECLUSAGE_BLENDWEIGHT[6] are float4.  This allows 
  236.     // up to 24 PCA weights and 1 float that gives the vertex shader 
  237.     // an index into the vertex's cluster's data
  238.     V( pInMesh->CloneMesh( pInMesh->GetOptions(), decl, pd3dDevice, &pOutMesh ) );
  239.  
  240.     // Make sure there are normals which are required for lighting
  241.     if( !(pInMesh->GetFVF() & D3DFVF_NORMAL) )
  242.         V( D3DXComputeNormals( pOutMesh, NULL ) );
  243.  
  244.     SAFE_RELEASE( pInMesh );
  245.  
  246.     *ppMesh = pOutMesh;
  247.  
  248.     return S_OK;
  249. }
  250.  
  251.  
  252. //--------------------------------------------------------------------------------------
  253. HRESULT CPRTMesh::LoadPRTBufferFromFile( WCHAR* strFile )
  254. {
  255.     HRESULT hr;
  256.     SAFE_RELEASE( m_pPRTBuffer );
  257.     SAFE_RELEASE( m_pPRTCompBuffer );
  258.  
  259.     WCHAR str[MAX_PATH];
  260.     V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, strFile ) );
  261.     wcscpy( m_ReloadState.strPRTBufferFileName, str );
  262.  
  263.     V( D3DXLoadPRTBufferFromFile( str, &m_pPRTBuffer ) );
  264.     m_dwOrder = GetOrderFromNumCoeffs( m_pPRTBuffer->GetNumCoeffs() );
  265.     return S_OK;
  266. }
  267.  
  268.  
  269. //--------------------------------------------------------------------------------------
  270. HRESULT CPRTMesh::LoadCompPRTBufferFromFile( WCHAR* strFile )
  271. {
  272.     HRESULT hr;
  273.     SAFE_RELEASE( m_pPRTBuffer );
  274.     SAFE_RELEASE( m_pPRTCompBuffer );
  275.  
  276.     WCHAR str[MAX_PATH];
  277.     V_RETURN( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, strFile ) );
  278.     wcscpy( m_ReloadState.strPRTBufferFileName, str );
  279.  
  280.     V( D3DXLoadPRTCompBufferFromFile( str, &m_pPRTCompBuffer ) );
  281.     m_ReloadState.bUseReloadState = true;
  282.     m_ReloadState.bLoadCompressed = true;
  283.     m_dwOrder = GetOrderFromNumCoeffs( m_pPRTCompBuffer->GetNumCoeffs() );
  284.     return S_OK;
  285. }
  286.  
  287.  
  288. //--------------------------------------------------------------------------------------
  289. void CPRTMesh::CompressBuffer( D3DXSHCOMPRESSQUALITYTYPE Quality, UINT NumClusters, UINT NumPCA )
  290.     HRESULT hr;
  291.     assert( m_pPRTBuffer != NULL );
  292.     SAFE_RELEASE(m_pPRTCompBuffer);
  293.     V( D3DXCreatePRTCompBuffer( Quality, NumClusters, NumPCA, m_pPRTBuffer, &m_pPRTCompBuffer ) );
  294.     m_ReloadState.quality = Quality;
  295.     m_ReloadState.dwNumClusters = NumClusters;
  296.     m_ReloadState.dwNumPCA = NumPCA;
  297.     m_ReloadState.bUseReloadState = true;
  298.     m_ReloadState.bLoadCompressed = false;
  299.     m_dwOrder = GetOrderFromNumCoeffs( m_pPRTBuffer->GetNumCoeffs() );
  300. }
  301.  
  302.  
  303. //--------------------------------------------------------------------------------------
  304. void CPRTMesh::SetPRTBuffer( ID3DXPRTBuffer* pPRTBuffer, WCHAR* strFile ) 
  305.     SAFE_RELEASE(m_pPRTBuffer);
  306.     SAFE_RELEASE(m_pPRTCompBuffer);
  307.     m_pPRTBuffer = pPRTBuffer;
  308.     m_dwOrder = GetOrderFromNumCoeffs( m_pPRTBuffer->GetNumCoeffs() );
  309.     wcsncpy( m_ReloadState.strPRTBufferFileName, strFile, MAX_PATH );
  310.     m_ReloadState.strPRTBufferFileName[MAX_PATH - 1] = L'\0';
  311. }
  312.  
  313.  
  314. //-----------------------------------------------------------------------------
  315. HRESULT CPRTMesh::LoadEffects( IDirect3DDevice9* pd3dDevice, const D3DCAPS9* pDeviceCaps )
  316. {
  317.     HRESULT hr;
  318.  
  319.     UINT dwNumChannels = m_pPRTCompBuffer->GetNumChannels();
  320.     UINT dwNumClusters = m_pPRTCompBuffer->GetNumClusters();
  321.     UINT dwNumPCA      = m_pPRTCompBuffer->GetNumPCA();
  322.  
  323.     // The number of vertex consts need by the shader can't exceed the 
  324.     // amount the HW can support
  325.     DWORD dwNumVConsts = dwNumClusters * (1 + dwNumChannels*dwNumPCA/4) + 4;
  326.     if( dwNumVConsts > pDeviceCaps->MaxVertexShaderConst )
  327.         return E_FAIL;
  328.  
  329.     SAFE_RELEASE( m_pPRTEffect );
  330.     SAFE_RELEASE( m_pSHIrradEnvMapEffect );
  331.     SAFE_RELEASE( m_pNDotLEffect );
  332.  
  333.     D3DXMACRO aDefines[3];
  334.     CHAR szMaxNumClusters[64];
  335.     sprintf( szMaxNumClusters, "%d", dwNumClusters );
  336.     szMaxNumClusters[63] = 0;
  337.     CHAR szMaxNumPCA[64];
  338.     sprintf( szMaxNumPCA, "%d", dwNumPCA );
  339.     szMaxNumPCA[63] = 0;
  340.     aDefines[0].Name       = "NUM_CLUSTERS";
  341.     aDefines[0].Definition = szMaxNumClusters;
  342.     aDefines[1].Name       = "NUM_PCA";
  343.     aDefines[1].Definition = szMaxNumPCA;
  344.     aDefines[2].Name       = NULL;
  345.     aDefines[2].Definition = NULL;
  346.  
  347.     // Define DEBUG_VS and/or DEBUG_PS to debug vertex and/or pixel shaders with the shader debugger.  
  348.     // Debugging vertex shaders requires either REF or software vertex processing, and debugging 
  349.     // pixel shaders requires REF.  The D3DXSHADER_FORCE_*_SOFTWARE_NOOPT flag improves the debug 
  350.     // experience in the shader debugger.  It enables source level debugging, prevents instruction 
  351.     // reordering, prevents dead code elimination, and forces the compiler to compile against the next 
  352.     // higher available software target, which ensures that the unoptimized shaders do not exceed 
  353.     // the shader model limitations.  Setting these flags will cause slower rendering since the shaders 
  354.     // will be unoptimized and forced into software.  See the DirectX documentation for more information 
  355.     // about using the shader debugger.
  356.     DWORD dwShaderFlags = 0;
  357.     #ifdef DEBUG_VS
  358.         dwShaderFlags |= D3DXSHADER_FORCE_VS_SOFTWARE_NOOPT;
  359.     #endif
  360.     #ifdef DEBUG_PS
  361.         dwShaderFlags |= D3DXSHADER_FORCE_PS_SOFTWARE_NOOPT;
  362.     #endif
  363.         
  364.     // Read the D3DX effect file
  365.     WCHAR str[MAX_PATH];
  366.     V( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, TEXT("PRTColorLights.fx") ) );
  367.  
  368.     // If this fails, there should be debug output as to 
  369.     // they the .fx file failed to compile
  370.     V( D3DXCreateEffectFromFile( pd3dDevice, str, aDefines, NULL, 
  371.                                  dwShaderFlags, NULL, &m_pPRTEffect, NULL ) );
  372.  
  373.     // Make sure the technique works on this card
  374.     hr = m_pPRTEffect->ValidateTechnique( "RenderWithPRTColorLights" );
  375.     if( FAILED( hr ) )
  376.         return DXTRACE_ERR( TEXT("ValidateTechnique"), hr );
  377.  
  378.     V( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, TEXT("SimpleLighting.fx") ) );
  379.     V( D3DXCreateEffectFromFile( pd3dDevice, str, NULL, NULL, 
  380.                                  dwShaderFlags, NULL, &m_pNDotLEffect, NULL ) );
  381.  
  382.     V( DXUTFindDXSDKMediaFileCch( str, MAX_PATH, TEXT("SHIrradianceEnvMap.fx") ) );
  383.     V( D3DXCreateEffectFromFile( pd3dDevice, str, NULL, NULL, 
  384.                                  dwShaderFlags, NULL, &m_pSHIrradEnvMapEffect, NULL ) );
  385.  
  386.     return S_OK;
  387. }
  388.  
  389.  
  390. //--------------------------------------------------------------------------------------
  391. void CPRTMesh::ExtractCompressedDataForPRTShader() 
  392.     HRESULT hr;
  393.  
  394.     // First call ID3DXPRTCompBuffer::NormalizeData.  This isn't nessacary, 
  395.     // but it makes it easier to use data formats that have little presision.
  396.     // It normalizes the PCA weights so that they are between [-1,1]
  397.     // and modifies the basis vectors accordingly.  
  398.     V( m_pPRTCompBuffer->NormalizeData() );
  399.  
  400.     UINT dwNumSamples  = m_pPRTCompBuffer->GetNumSamples();
  401.     UINT dwNumCoeffs   = m_pPRTCompBuffer->GetNumCoeffs();
  402.     UINT dwNumChannels = m_pPRTCompBuffer->GetNumChannels();
  403.     UINT dwNumClusters = m_pPRTCompBuffer->GetNumClusters();
  404.     UINT dwNumPCA      = m_pPRTCompBuffer->GetNumPCA();
  405.  
  406.     // With clustered PCA, each vertex is assigned to a cluster.  To figure out 
  407.     // which vertex goes with which cluster, call ID3DXPRTCompBuffer::ExtractClusterIDs.
  408.     // This will return a cluster ID for every vertex.  Simply pass in an array of UINTs
  409.     // that is the size of the number of vertices (which also equals the number of samples), and 
  410.     // the cluster ID for vertex N will be at puClusterIDs[N].
  411.     UINT* pClusterIDs = new UINT[ dwNumSamples ];
  412.     assert( pClusterIDs );
  413.     V( m_pPRTCompBuffer->ExtractClusterIDs( pClusterIDs ) );
  414.  
  415.     D3DVERTEXELEMENT9 declCur[MAX_FVF_DECL_SIZE];
  416.     m_pMesh->GetDeclaration( declCur );
  417.  
  418.     // Now use this cluster ID info to store a value in the mesh in the 
  419.     // D3DDECLUSAGE_BLENDWEIGHT[0] which is declared in the vertex decl to be a float1
  420.     // This value will be passed into the vertex shader to allow the shader 
  421.     // use this number as an offset into an array of shader constants.  
  422.     // The value we set per vertex is based on the cluster ID and the stride 
  423.     // of the shader constants array.  
  424.     BYTE* pV = NULL;
  425.     V( m_pMesh->LockVertexBuffer( 0, (void**) &pV ) );
  426.     UINT uStride = m_pMesh->GetNumBytesPerVertex();
  427.     BYTE* pClusterID = pV + 32; // 32 == D3DDECLUSAGE_BLENDWEIGHT[0] offset
  428.     for( UINT uVert = 0; uVert < dwNumSamples; uVert++ ) 
  429.     {
  430.         float fArrayOffset = (float)(pClusterIDs[uVert] * (1+3*(dwNumPCA/4))); 
  431.         memcpy(pClusterID, &fArrayOffset, sizeof(float));
  432.         pClusterID += uStride;
  433.     }
  434.     m_pMesh->UnlockVertexBuffer();
  435.     SAFE_DELETE_ARRAY(pClusterIDs);
  436.  
  437.     // Now we also need to store the per vertex PCA weights.  Earilier when
  438.     // the mesh was loaded, we changed the vertex decl to make room to store these
  439.     // PCA weights.  In this sample, we will use D3DDECLUSAGE_BLENDWEIGHT[1] to 
  440.     // D3DDECLUSAGE_BLENDWEIGHT[6].  Using D3DDECLUSAGE_BLENDWEIGHT intead of some other 
  441.     // usage was an arbritatey decision.  Since D3DDECLUSAGE_BLENDWEIGHT[1-6] were 
  442.     // declared as float4 then we can store up to 6*4 PCA weights per vertex.  They don't
  443.     // have to be declared as float4, but its a reasonable choice.  So for example, 
  444.     // if dwNumPCAVectors=16 the function will write data to D3DDECLUSAGE_BLENDWEIGHT[1-4]
  445.     V( m_pPRTCompBuffer->ExtractToMesh( dwNumPCA, D3DDECLUSAGE_BLENDWEIGHT, 1, m_pMesh ) );
  446.  
  447.     // Extract the cluster bases into a large array of floats.  
  448.     // ID3DXPRTCompBuffer::ExtractBasis will extract the basis 
  449.     // for a single cluster.  
  450.     //
  451.     // A single cluster basis is an array of
  452.     // (NumPCA+1)*NumCoeffs*NumChannels floats
  453.     // The "1+" is for the cluster mean.
  454.     int nClusterBasisSize = (dwNumPCA+1) * dwNumCoeffs * dwNumChannels;  
  455.     int nBufferSize       = nClusterBasisSize * dwNumClusters; 
  456.  
  457.     SAFE_DELETE_ARRAY( m_aClusterBases );
  458.     m_aClusterBases = new float[nBufferSize];
  459.     assert( m_aClusterBases );
  460.  
  461.     for( DWORD iCluster = 0; iCluster < dwNumClusters; iCluster++ ) 
  462.     {
  463.         // ID3DXPRTCompBuffer::ExtractBasis() extracts the basis for a single cluster at a time.
  464.         V( m_pPRTCompBuffer->ExtractBasis( iCluster, &m_aClusterBases[iCluster * nClusterBasisSize] ) );
  465.     }
  466.  
  467.     SAFE_DELETE_ARRAY( m_aPRTConstants );
  468.     m_aPRTConstants = new float[dwNumClusters*(4+dwNumChannels*dwNumPCA)];
  469.     assert( m_aPRTConstants );
  470. }
  471.  
  472.  
  473. //--------------------------------------------------------------------------------------
  474. void CPRTMesh::ComputeSHIrradEnvMapConstants( float* pSHCoeffsRed, float* pSHCoeffsGreen, float* pSHCoeffsBlue )
  475.     HRESULT hr;
  476.  
  477.     float* fLight[3] = { pSHCoeffsRed, pSHCoeffsGreen, pSHCoeffsBlue };
  478.  
  479.     // Lighting environment coefficients
  480.     D3DXVECTOR4 vCoefficients[3];
  481.  
  482.     // These constants are described in the article by Peter-Pike Sloan titled 
  483.     // "Efficient Evaluation of Irradiance Environment Maps" in the book 
  484.     // "ShaderX 2 - Shader Programming Tips and Tricks" by Wolfgang F. Engel.
  485.     static const float s_fSqrtPI = ((float)sqrtf(D3DX_PI));
  486.     const float fC0 = 1.0f/(2.0f*s_fSqrtPI);
  487.     const float fC1 = (float)sqrt(3.0f)/(3.0f*s_fSqrtPI);
  488.     const float fC2 = (float)sqrt(15.0f)/(8.0f*s_fSqrtPI);
  489.     const float fC3 = (float)sqrt(5.0f)/(16.0f*s_fSqrtPI);
  490.     const float fC4 = 0.5f*fC2;
  491.  
  492.     for( int iChannel=0; iChannel<3; iChannel++ )
  493.     {
  494.         vCoefficients[iChannel].x = -fC1*fLight[iChannel][3];
  495.         vCoefficients[iChannel].y = -fC1*fLight[iChannel][1];
  496.         vCoefficients[iChannel].z =  fC1*fLight[iChannel][2];
  497.         vCoefficients[iChannel].w =  fC0*fLight[iChannel][0] - fC3*fLight[iChannel][6];
  498.     }
  499.  
  500.     V( m_pSHIrradEnvMapEffect->SetVector( "cAr", &vCoefficients[0] ) );
  501.     V( m_pSHIrradEnvMapEffect->SetVector( "cAg", &vCoefficients[1] ) );
  502.     V( m_pSHIrradEnvMapEffect->SetVector( "cAb", &vCoefficients[2] ) );
  503.  
  504.     for( iChannel=0; iChannel<3; iChannel++ )
  505.     {
  506.         vCoefficients[iChannel].x =      fC2*fLight[iChannel][4];
  507.         vCoefficients[iChannel].y =     -fC2*fLight[iChannel][5];
  508.         vCoefficients[iChannel].z = 3.0f*fC3*fLight[iChannel][6];
  509.         vCoefficients[iChannel].w =     -fC2*fLight[iChannel][7];
  510.     }
  511.  
  512.     V( m_pSHIrradEnvMapEffect->SetVector( "cBr", &vCoefficients[0] ) );
  513.     V( m_pSHIrradEnvMapEffect->SetVector( "cBg", &vCoefficients[1] ) );
  514.     V( m_pSHIrradEnvMapEffect->SetVector( "cBb", &vCoefficients[2] ) );
  515.  
  516.     vCoefficients[0].x = fC4*fLight[0][8];
  517.     vCoefficients[0].y = fC4*fLight[1][8];
  518.     vCoefficients[0].z = fC4*fLight[2][8];
  519.     vCoefficients[0].w = 1.0f;
  520.  
  521.     V( m_pSHIrradEnvMapEffect->SetVector( "cC", &vCoefficients[0] ) );
  522. }
  523.  
  524.  
  525. //--------------------------------------------------------------------------------------
  526. void CPRTMesh::ComputeShaderConstants( float* pSHCoeffsRed, float* pSHCoeffsGreen, float* pSHCoeffsBlue, DWORD dwNumCoeffsPerChannel )
  527. {
  528.     HRESULT hr;
  529.     assert( dwNumCoeffsPerChannel == m_pPRTCompBuffer->GetNumCoeffs() );
  530.  
  531.     UINT dwNumCoeffs   = m_pPRTCompBuffer->GetNumCoeffs();
  532.     UINT dwOrder       = m_dwOrder;
  533.     UINT dwNumChannels = m_pPRTCompBuffer->GetNumChannels();
  534.     UINT dwNumClusters = m_pPRTCompBuffer->GetNumClusters();
  535.     UINT dwNumPCA      = m_pPRTCompBuffer->GetNumPCA();
  536.  
  537.     //
  538.     // With compressed PRT, a single diffuse channel is caluated by:
  539.     //       R[p] = (M[k] dot L') + sum( w[p][j] * (B[k][j] dot L');
  540.     // where the sum runs j between 0 and # of PCA vectors
  541.     //       R[p] = exit radiance at point p
  542.     //       M[k] = mean of cluster k 
  543.     //       L' = source radiance approximated with SH coefficients
  544.     //       w[p][j] = the j'th PCA weight for point p
  545.     //       B[k][j] = the j'th PCA basis vector for cluster k
  546.     //
  547.     // Note: since both (M[k] dot L') and (B[k][j] dot L') can be computed on the CPU, 
  548.     // these values are passed as constants using the array m_aPRTConstants.   
  549.     // 
  550.     // So we compute an array of floats, m_aPRTConstants, here.
  551.     // This array is the L' dot M[k] and L' dot B[k][j].
  552.     // The source radiance is the lighting environment in terms of spherical
  553.     // harmonic coefficients which can be computed with D3DXSHEval* or D3DXSHProjectCubeMap.  
  554.     // M[k] and B[k][j] are also in terms of spherical harmonic basis coefficients 
  555.     // and come from ID3DXPRTCompBuffer::ExtractBasis().
  556.     //
  557.     DWORD dwClusterStride = dwNumChannels*dwNumPCA + 4;
  558.     DWORD dwBasisStride = dwNumCoeffs*dwNumChannels*(dwNumPCA + 1);  
  559.  
  560.     for( DWORD iCluster = 0; iCluster < dwNumClusters; iCluster++ ) 
  561.     {
  562.         // For each cluster, store L' dot M[k] per channel, where M[k] is the mean of cluster k
  563.         m_aPRTConstants[iCluster*dwClusterStride + 0] = D3DXSHDot( dwOrder, &m_aClusterBases[iCluster*dwBasisStride + 0*dwNumCoeffs], pSHCoeffsRed );
  564.         m_aPRTConstants[iCluster*dwClusterStride + 1] = D3DXSHDot( dwOrder, &m_aClusterBases[iCluster*dwBasisStride + 1*dwNumCoeffs], pSHCoeffsGreen );
  565.         m_aPRTConstants[iCluster*dwClusterStride + 2] = D3DXSHDot( dwOrder, &m_aClusterBases[iCluster*dwBasisStride + 2*dwNumCoeffs], pSHCoeffsBlue );
  566.         m_aPRTConstants[iCluster*dwClusterStride + 3] = 0.0f;
  567.  
  568.         // Then per channel we compute L' dot B[k][j], where B[k][j] is the jth PCA basis vector for cluster k
  569.         float* pPCAStart = &m_aPRTConstants[iCluster*dwClusterStride + 4];
  570.         for( DWORD iPCA = 0; iPCA < dwNumPCA; iPCA++ ) 
  571.         {
  572.             int nOffset = iCluster*dwBasisStride + (iPCA+1)*dwNumCoeffs*dwNumChannels;
  573.  
  574.             pPCAStart[0*dwNumPCA + iPCA] = D3DXSHDot( dwOrder, &m_aClusterBases[nOffset + 0*dwNumCoeffs], pSHCoeffsRed );
  575.             pPCAStart[1*dwNumPCA + iPCA] = D3DXSHDot( dwOrder, &m_aClusterBases[nOffset + 1*dwNumCoeffs], pSHCoeffsGreen );
  576.             pPCAStart[2*dwNumPCA + iPCA] = D3DXSHDot( dwOrder, &m_aClusterBases[nOffset + 2*dwNumCoeffs], pSHCoeffsBlue );
  577.         }
  578.     }
  579.  
  580.     V( m_pPRTEffect->SetFloatArray( "aPRTConstants", (float*)m_aPRTConstants, dwNumClusters*(4+dwNumChannels*dwNumPCA) ) );
  581. }
  582.  
  583.  
  584. //--------------------------------------------------------------------------------------
  585. void CPRTMesh::RenderWithPRT( IDirect3DDevice9* pd3dDevice, D3DXMATRIX* pmWorldViewProj, bool bRenderWithAlbedo )
  586. {
  587.     HRESULT hr;
  588.     UINT iPass, cPasses;
  589.  
  590.     m_pPRTEffect->SetMatrix( "g_mWorldViewProjection", pmWorldViewProj );
  591.  
  592.     bool bHasAlbedoTexture = false;
  593.     for(int i=0; i<m_pAlbedoTextures.GetSize(); i++ )
  594.     {
  595.         if( m_pAlbedoTextures.GetAt(i) != NULL )
  596.             bHasAlbedoTexture = true;
  597.     }
  598.     if( !bHasAlbedoTexture )
  599.         bRenderWithAlbedo = false;
  600.  
  601.     if( bRenderWithAlbedo )
  602.     {
  603.         V( m_pPRTEffect->SetTechnique( "RenderWithPRTColorLights" ) );
  604.     }
  605.     else
  606.     {
  607.         V( m_pPRTEffect->SetTechnique( "RenderWithPRTColorLightsNoAlbedo" ) );
  608.     }
  609.  
  610.     if( !bRenderWithAlbedo )
  611.     {
  612.         D3DXCOLOR clrWhite = D3DXCOLOR(1,1,1,1);
  613.         V( m_pPRTEffect->SetValue("MaterialDiffuseColor", &clrWhite, sizeof(D3DCOLORVALUE) ) );
  614.     }
  615.  
  616.     V( m_pPRTEffect->Begin(&cPasses, 0) );
  617.  
  618.     for (iPass = 0; iPass < cPasses; iPass++)
  619.     {
  620.         V( m_pPRTEffect->BeginPass(iPass) );
  621.  
  622.         DWORD dwAttribs = 0;
  623.         V( m_pMesh->GetAttributeTable( NULL, &dwAttribs ) );
  624.         for( DWORD i=0; i<dwAttribs; i++ )
  625.         {            
  626.             if( bRenderWithAlbedo )
  627.             {
  628.                 if( m_pAlbedoTextures.GetSize() > (int) i )
  629.                     V( m_pPRTEffect->SetTexture( "AlbedoTexture", m_pAlbedoTextures.GetAt(i) ) );
  630.  
  631.                 V( m_pPRTEffect->SetValue("MaterialDiffuseColor", &m_pMaterials[i].MatD3D.Diffuse, sizeof(D3DCOLORVALUE) ) );
  632.                 V( m_pPRTEffect->CommitChanges() );
  633.             }
  634.             V( m_pMesh->DrawSubset(i) );
  635.         }
  636.  
  637.         V( m_pPRTEffect->EndPass() );
  638.     }
  639.  
  640.     V( m_pPRTEffect->End() );
  641. }
  642.  
  643.  
  644. //--------------------------------------------------------------------------------------
  645. void CPRTMesh::RenderWithSHIrradEnvMap( IDirect3DDevice9* pd3dDevice, D3DXMATRIX* pmWorldViewProj, bool bRenderWithAlbedo )
  646. {
  647.     HRESULT hr;
  648.     UINT iPass, cPasses;
  649.  
  650.     m_pSHIrradEnvMapEffect->SetMatrix( "g_mWorldViewProjection", pmWorldViewProj );
  651.  
  652.     bool bHasAlbedoTexture = false;
  653.     for(int i=0; i<m_pAlbedoTextures.GetSize(); i++ )
  654.     {
  655.         if( m_pAlbedoTextures.GetAt(i) != NULL )
  656.             bHasAlbedoTexture = true;
  657.     }
  658.     if( !bHasAlbedoTexture )
  659.         bRenderWithAlbedo = false;
  660.  
  661.     if( bRenderWithAlbedo )
  662.     {
  663.         V( m_pSHIrradEnvMapEffect->SetTechnique( "RenderWithSHIrradEnvMap" ) );
  664.     }
  665.     else
  666.     {
  667.         V( m_pSHIrradEnvMapEffect->SetTechnique( "RenderWithSHIrradEnvMapNoAlbedo" ) );
  668.     }
  669.  
  670.     if( !bRenderWithAlbedo )
  671.     {
  672.         D3DXCOLOR clrWhite = D3DXCOLOR(1,1,1,1);
  673.         V( m_pSHIrradEnvMapEffect->SetValue("MaterialDiffuseColor", &clrWhite, sizeof(D3DCOLORVALUE) ) );
  674.     }
  675.  
  676.     V( m_pSHIrradEnvMapEffect->Begin(&cPasses, 0) );
  677.  
  678.     for (iPass = 0; iPass < cPasses; iPass++)
  679.     {
  680.         V( m_pSHIrradEnvMapEffect->BeginPass(iPass) );
  681.  
  682.         DWORD dwAttribs = 0;
  683.         V( m_pMesh->GetAttributeTable( NULL, &dwAttribs ) );
  684.         for( DWORD i=0; i<dwAttribs; i++ )
  685.         {
  686.             if( bRenderWithAlbedo )
  687.             {
  688.                 if( m_pAlbedoTextures.GetSize() > (int) i )
  689.                     V( m_pSHIrradEnvMapEffect->SetTexture( "AlbedoTexture", m_pAlbedoTextures.GetAt(i) ) );
  690.                 V( m_pSHIrradEnvMapEffect->SetValue("MaterialDiffuseColor", &m_pMaterials[i].MatD3D.Diffuse, sizeof(D3DCOLORVALUE) ) );
  691.                 V( m_pSHIrradEnvMapEffect->CommitChanges() );
  692.             }
  693.             V( m_pMesh->DrawSubset(i) );
  694.         }
  695.  
  696.         V( m_pSHIrradEnvMapEffect->EndPass() );
  697.     }
  698.  
  699.     V( m_pSHIrradEnvMapEffect->End() );
  700. }
  701.  
  702.  
  703. //--------------------------------------------------------------------------------------
  704. void CPRTMesh::RenderWithNdotL( IDirect3DDevice9* pd3dDevice, D3DXMATRIX* pmWorldViewProj, D3DXMATRIX* pmWorldInv, bool bRenderWithAlbedo, CDXUTDirectionWidget* aLightControl, int nNumLights, float fLightScale )
  705. {
  706.     HRESULT hr;
  707.     UINT iPass, cPasses;
  708.  
  709.     m_pNDotLEffect->SetMatrix( "g_mWorldViewProjection", pmWorldViewProj );
  710.     m_pNDotLEffect->SetMatrix( "g_mWorldInv", pmWorldInv );
  711.  
  712.     D3DXVECTOR4 vLightDir[10];
  713.     D3DXVECTOR4 vLightsDiffuse[10];
  714.     D3DXVECTOR4 lightOn(1,1,1,1);
  715.     D3DXVECTOR4 lightOff(0,0,0,0);
  716.     lightOn *= fLightScale;
  717.  
  718.     for( int i=0; i<nNumLights; i++ )
  719.         vLightDir[i] = D3DXVECTOR4( aLightControl[i].GetLightDirection(), 0 );
  720.     for( int i=0; i<10; i++ )
  721.         vLightsDiffuse[i] = (nNumLights > i) ? lightOn : lightOff;
  722.  
  723.     bool bHasAlbedoTexture = false;
  724.     for(int i=0; i<m_pAlbedoTextures.GetSize(); i++ )
  725.     {
  726.         if( m_pAlbedoTextures.GetAt(i) != NULL )
  727.             bHasAlbedoTexture = true;
  728.     }
  729.     if( !bHasAlbedoTexture )
  730.         bRenderWithAlbedo = false;
  731.  
  732.     if( bRenderWithAlbedo )
  733.     {
  734.         V( m_pNDotLEffect->SetTechnique( "RenderWithNdotL" ) );
  735.     }
  736.     else
  737.     {
  738.         V( m_pNDotLEffect->SetTechnique( "RenderWithNdotLNoAlbedo" ) );
  739.     }
  740.  
  741.     if( !bRenderWithAlbedo )
  742.     {
  743.         D3DXCOLOR clrWhite = D3DXCOLOR(1,1,1,1);
  744.         V( m_pNDotLEffect->SetValue("MaterialDiffuseColor", &clrWhite, sizeof(D3DCOLORVALUE) ) );
  745.     }
  746.  
  747.     V( m_pNDotLEffect->Begin(&cPasses, 0) );
  748.  
  749.     for (iPass = 0; iPass < cPasses; iPass++)
  750.     {
  751.         V( m_pNDotLEffect->BeginPass(iPass) );
  752.  
  753.         // 10 and 20 are the register constants
  754.         V( pd3dDevice->SetVertexShaderConstantF( 10, (float*)vLightDir, nNumLights ) );
  755.         V( pd3dDevice->SetVertexShaderConstantF( 20, (float*)vLightsDiffuse, 10 ) );
  756.  
  757.         DWORD dwAttribs = 0;
  758.         V( m_pMesh->GetAttributeTable( NULL, &dwAttribs ) );
  759.         for( DWORD i=0; i<dwAttribs; i++ )
  760.         {
  761.             if( bRenderWithAlbedo )
  762.             {
  763.                 if( m_pAlbedoTextures.GetSize() > (int) i )
  764.                     V( m_pNDotLEffect->SetTexture( "AlbedoTexture", m_pAlbedoTextures.GetAt(i) ) );
  765.                 V( m_pNDotLEffect->SetValue("MaterialDiffuseColor", &m_pMaterials[i].MatD3D.Diffuse, sizeof(D3DCOLORVALUE) ) );
  766.                 V( m_pNDotLEffect->CommitChanges() );
  767.             }
  768.             V( m_pMesh->DrawSubset(i) );
  769.         }
  770.  
  771.         V( m_pNDotLEffect->EndPass() );
  772.     }
  773.  
  774.     V( m_pNDotLEffect->End() );
  775. }
  776.  
  777.  
  778. //--------------------------------------------------------------------------------------
  779. void CPRTMesh::OnLostDevice()
  780. {
  781.     HRESULT hr;
  782.     if( m_pPRTEffect )
  783.         V( m_pPRTEffect->OnLostDevice() );
  784.     if( m_pSHIrradEnvMapEffect )
  785.         V( m_pSHIrradEnvMapEffect->OnLostDevice() );
  786.     if( m_pNDotLEffect )
  787.         V( m_pNDotLEffect->OnLostDevice() );
  788. }
  789.  
  790.  
  791. //--------------------------------------------------------------------------------------
  792. void CPRTMesh::OnDestroyDevice()
  793. {
  794.     if( !m_ReloadState.bUseReloadState )
  795.         ZeroMemory( &m_ReloadState, sizeof(RELOAD_STATE) );
  796.  
  797.     SAFE_RELEASE( m_pMesh );
  798.     for(int i=0; i<m_pAlbedoTextures.GetSize(); i++ )
  799.     {
  800.         SAFE_RELEASE( m_pAlbedoTextures[i] );
  801.     }
  802.     m_pAlbedoTextures.RemoveAll();
  803.     SAFE_RELEASE( m_pMaterialBuffer );
  804.     SAFE_RELEASE( m_pPRTBuffer );
  805.     SAFE_RELEASE( m_pPRTCompBuffer );
  806.     SAFE_RELEASE( m_pPRTEffect );
  807.     SAFE_RELEASE( m_pSHIrradEnvMapEffect );
  808.     SAFE_RELEASE( m_pNDotLEffect );
  809. }
  810.  
  811.  
  812. //--------------------------------------------------------------------------------------
  813. UINT CPRTMesh::GetOrderFromNumCoeffs( UINT dwNumCoeffs )
  814. {
  815.     UINT dwOrder=1; 
  816.     while(dwOrder*dwOrder < dwNumCoeffs) 
  817.         dwOrder++;
  818.  
  819.     return dwOrder;
  820. }
  821.  
  822.  
  823.